package com.tim.helper;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *@describe 反射，通过注解获取信息；拼接；
 * 复制到controller处，运行main方法；输出到 L:\temp\postmanJson
 *@author Tim
 *@create 2022/9/22 15:59
 */
@Data
public class ControllerClass2Postman {
	static Class<?> controllerClazz = ControllerClass2Postman.class;
	static String clzPath = "" +
			"" +
			"" +
			"";
	static String HOST_NAME = "{{host}}";
	// headerMap，v不需要{}
	static Map<String, String> headerMap = MapUtil
			.builder("token", "token")
			.put("cookie", "cookie")
			.put("userName", "userName")
			.build();
	static String TEST_SCRIPT = "tests.ok=pm.response.json().errCode===0";
	static String outputDir = "C:/aa";

	/**
	 * @description 获取当前服务所有接口，构造postman服务
	 * 这里可以选择当前所有服务的接口所在接口名，或者单独某个接口的类名
	 */
	public static void main(String[] args) throws Exception {

		controller2Postman();
	}

	private static void controller2Postman() throws IOException {
		// 类描述
		String classDesc;
		Api api = controllerClazz.getDeclaredAnnotation(Api.class);
		if(api!=null) {
			classDesc = api.tags()[0];
		}else {
			classDesc = controllerClazz.getSimpleName();
		}
		Map<String, Object> map = new HashMap<>();
		map.put("id", UUID.randomUUID().toString());
		map.put("name", classDesc);

		try {
			//获取对象中的所有方法，并排序
			Method[] methods = controllerClazz.getDeclaredMethods();
			{
				Map<String, Method> methodMap = Arrays.stream(methods)
						.collect(Collectors.toMap(Method::getName, Function.identity()));
				List<String> content = Files.readAllLines(Paths.get(clzPath));
				int i=0;
				for (String line : content){
					line = line.trim();
					if(line.startsWith("public ") && !line.contains(" static ") && line.contains("(")){
						line = line.substring(0, line.lastIndexOf('(')).trim();
						String methodName = line.substring(line.lastIndexOf(" ")).trim();
						Method method = methodMap.get(methodName);
						if(method!=null){
							methods[i++] = method;
						}
					}
				}
				if(i<methods.length){
					throw new RuntimeException("methods error");
				}
			}

			//解析方法参数
			RequestMapping mapping = controllerClazz.getDeclaredAnnotation(RequestMapping.class);
			String classReqPath = mapping!=null ? mapping.value()[0] : "";
			List<Map<String, Object>> postmanReqItems = genPostmanReqItems(methods, classReqPath);
			map.put("requests", postmanReqItems);
		} catch (Exception e) {
			e.printStackTrace();
		}

		map = v1Tov2(map, classDesc);
		File file = new File(outputDir);
		if(!file.exists()) {
			file.mkdirs();
		}
		classDesc = classDesc
				.replaceAll(":", "0")
				.replaceAll("[\\\\/]", "、");
		Files.write(Paths.get(outputDir, classDesc+".json"), Arrays.asList(JSON.toJSONString(map)));
	}

	/**
	 * @description 设置基本参数
	 */
	private static Map<String, Object> createBaseReqItem(Method method) {
		Map<String, Object> postmanReqItem = new HashMap<>();
		postmanReqItem.put("id", UUID.randomUUID().toString());
		postmanReqItem.put("url", "");
		postmanReqItem.put("dataMode", "raw");
		postmanReqItem.put("method", "POST");
		StringBuilder headers = new StringBuilder("Content-Type:application/json");
		headerMap.forEach((k,v)->headers.append(";").append(k+":{{"+v+"}}"));
		postmanReqItem.put("headers", headers.toString());

		postmanReqItem.put("rawModeData", "{\n" + "    \"id\": 1\n" + "}");
		return postmanReqItem;
	}

	/**
	 * @param methods
	 * @param classReqPath
	 * @description 生成postman请求
	 */
	private static List<Map<String, Object>> genPostmanReqItems(Method[] methods, String classReqPath) throws Exception {
		List<Map<String, Object>> postmanReqItems = new ArrayList<>();

		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			//基本参数设值
			Map<String, Object> postmanReqItem = createBaseReqItem(method);
			postmanReqItem.put("name", method.getName());//获取方法名字

			ApiOperation anno = method.getAnnotation(ApiOperation.class);
			if(anno!=null) {
				postmanReqItem.put("name", anno.value());
			}
			setReqInfo(method, postmanReqItem, classReqPath);
			postmanReqItems.add(postmanReqItem);
		}

		return postmanReqItems;
	}

	static List<Class<? extends Annotation>> list = Arrays.asList(RequestMapping.class, PostMapping.class,
			GetMapping.class, DeleteMapping.class, PutMapping.class);
	//设置请求方法，请求url
	private static void setReqInfo(Method method, Map<String, Object> postmanReqItem, String classReqPath)
			throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

		//请求注解，获取请求方法、url
		Annotation reqAnnotation = null;
		for (int j = 0; j < list.size(); j++) {
			Class<? extends Annotation> clz = list.get(j);
			reqAnnotation = method.getAnnotation(clz);
			if (reqAnnotation != null) {
				String url = "";
				Class<? extends Annotation> reqType = reqAnnotation.annotationType();
				String mtdName = "";
				if(reqType.equals(RequestMapping.class)) {
					Method m = reqType.getMethod("method");
					Object obj = m.invoke(reqAnnotation);
					if(!ObjectUtils.isEmpty(obj)) {
						RequestMethod rMtds = ((RequestMethod[])obj)[0];
						mtdName = rMtds.name();
					}else {
						mtdName = "GET";
					}
				}else {
					mtdName = reqType.getSimpleName().replace("Mapping", "");
				}
				Method mtd = reqType.getMethod("value");
				Object obj = mtd.invoke(reqAnnotation);
				if(!ObjectUtils.isEmpty(obj)) {
					url = ((String[])obj)[0];
					url = url.startsWith("/") ? url.substring(1) : url;
					if(url.contains("：")){
						url = url.split("：")[1].trim();
					}
				}
				postmanReqItem.put("method", mtdName);
				postmanReqItem.put("url", String.format("%s/%s/%s", HOST_NAME, classReqPath, url).replaceAll("//", "/"));
				break;
			}
		}
	}

	public static Map<String, Object> v1Tov2(Map<String, Object> origin, String classDesc){
		Map<String, Object> result = new LinkedHashMap<>();
		Map<String, Object> info = MapUtil.builder("_postman_id", origin.get("id"))
				.put("name", origin.get("name"))
				.put("schema", "https://schema.getpostman.com/json/collection/v2.1.0/collection.json")
				.build();
		List<Map<String, Object>> requests = (List<Map<String, Object>>) origin.get("requests");
		List<Map<String, Object>> item = new ArrayList<>();
		for (Map<String, Object> originRequest:requests) {
			Map<String, Object> request = new HashMap<>(originRequest);
			String originHeaders = Objects.toString(originRequest.get("headers"), "");
			String[] headerItems = originHeaders.split(";");
			List<Object> headers = new ArrayList<>();
			for(String headerItem:headerItems){
				String[] strs = headerItem.split(":");
				if(strs.length==2) {
					headers.add(MapUtil.builder("key", strs[0]).put("value", strs[1]).build());
				}
			}
			request.put("headers", headers);
			request.remove("dataMode");
			Object rawModeData = request.remove("rawModeData");
			Map<String, Object> body = MapUtil.builder("raw", rawModeData)
					.put("mode", "raw")
					.build();
			request.put("body", body);
			// 测试
			Map<String, Object> testEvent = new LinkedHashMap<>();
			testEvent.put("listen", "test");
			Map<String, Object> script = new LinkedHashMap<>();
//			script.put("id", UUID.randomUUID().toString());
			script.put("exec", new String[]{TEST_SCRIPT});
			script.put("type", "text/javascript");
			testEvent.put("script", script);
			Map<String, Object> newReq = MapUtil.builder("name", request.get("name"))
					.put("event", new Object[]{testEvent})
					.put("request", request).build();
			item.add(newReq);
//			Map<String, Object> testCsvReq = getCsvReq(newReq);
//			item.add(testCsvReq);

		}

		result.put("info", info);
		// folder
		Map itemMap = new HashMap();
		itemMap.put("name", classDesc);
		itemMap.put("item", item);
		result.put("item", Collections.singletonList(itemMap));
		return result;
	}



}
